<?php
/**
 * Copyright (c) 2011 Bart Visscher <bartv@thisnet.nl>
 * Copyright (c) 2012 Georg Ehrke <georg@owncloud.com>
 * This file is licensed under the Affero General Public License version 3 or
 * later.
 * See the COPYING-README file.
 *
 * This class manages our app actions
 */
class PCalendarModelApp extends JModelLegacy
{
   const CALENDAR = 'calendar';
   const EVENT = 'event';
   /**
    * @brief language object for calendar app
    */
   public static $l10n;

   /**
    * @brief categories of the user
    */
   protected static $categories = null;

   /**
    * @brief timezone of the user
    */
   public static $tz;

   /**
    * @brief config of PFactory
    */
   public static $config;

   function __construct()
   {
      self::$l10n = new OC_L10N('calendar');
      self::$config = PFactory::getConfig();
      self::$tz = self::$config->get('offset', 'Asia/Shanghai');
   }

   /**
    * @brief returns informations about a calendar
    * @param int $id - id of the calendar
    * @param bool $security - check access rights or not
    * @param bool $shared - check if the user got access via sharing
    * @return mixed - bool / array
    */
   public function getCalendar($id, $security = true, $shared = false) {
      if(! is_numeric($id)) {
         return false;
      }
      $calendar_model = new PCalendarModelCalendar();

      $calendar = $calendar_model->find($id);
      // FIXME: Correct arguments to just check for permissions
      if($security === true && $shared === false) {
         if(OC_USER::getUser() === $calendar['userid']){
            return $calendar;
         }else{
            return false;
         }
      }
      if($security === true && $shared === true) {
         if(OCP\Share::getItemSharedWithBySource('calendar', $id)) {
            return $calendar;
         }
      }
      return $calendar;
   }

   /**
    * @brief returns informations about an event
    * @param int $id - id of the event
    * @param bool $security - check access rights or not
    * @param bool $shared - check if the user got access via sharing
    * @return mixed - bool / array
    */
   public function getEventObject($id, $security = true, $shared = false) {
      $object_model = new PCalendarModelObject();
      $event = $object_model->find($id);
      if($shared === true || $security === true) {
         $permissions = $this->getPermissions($id, self::EVENT);
         OCP\Util::writeLog('contacts', __METHOD__.' id: '.$id.', permissions: '.$permissions, OCP\Util::DEBUG);
         if($this->getPermissions($id, self::EVENT)) {
            return $event;
         }
      } else {
         return $event;
      }

      return false;
   }

   /**
    * @brief returns the parsed calendar data
    * @param int $id - id of the event
    * @param bool $security - check access rights or not
    * @return mixed - bool / object
    */
   public function getVCalendar($id, $security = true, $shared = false) {
      $event_object = $this->getEventObject($id, $security, $shared);
      if($event_object === false) {
         return false;
      }
      $vobject = OC_VObject::parse($event_object['calendardata']);
      if(is_null($vobject)) {
         return false;
      }
      return $vobject;
   }

   /**
    * @brief checks if an event was edited and dies if it was
    * @param (object) $vevent - vevent object of the event
    * @param (int) $lastmodified - time of last modification as unix timestamp
    * @return (bool)
    */
   public function isNotModified($vevent, $lastmodified) {
      $last_modified = $vevent->__get('LAST-MODIFIED');
      if($last_modified && $lastmodified != $last_modified->getDateTime()->format('U')) {
         $this->setError("该面试已被其他面试官更新，请刷新后重新编辑！");
         return false;
      }
      return true;
   }

   /**
    * @brief returns the default categories of ownCloud
    * @return (array) $categories
    */
   public function getDefaultCategories() {
      return array(
         (string)self::$l10n->t('Birthday'),
         (string)self::$l10n->t('Business'),
         (string)self::$l10n->t('Call'),
         (string)self::$l10n->t('Clients'),
         (string)self::$l10n->t('Deliverer'),
         (string)self::$l10n->t('Holidays'),
         (string)self::$l10n->t('Ideas'),
         (string)self::$l10n->t('Journey'),
         (string)self::$l10n->t('Jubilee'),
         (string)self::$l10n->t('Meeting'),
         (string)self::$l10n->t('Other'),
         (string)self::$l10n->t('Personal'),
         (string)self::$l10n->t('Projects'),
         (string)self::$l10n->t('Questions'),
         (string)self::$l10n->t('Work'),
      );
   }

   /**
    * @brief returns the vcategories object of the user
    * @return (object) $vcategories
    */
   public function getVCategories() {
      if (is_null(self::$categories)) {
         if(OC_VCategories::isEmpty('event')) {
            $this->scanCategories();
         }
         self::$categories = new OC_VCategories('event',
            null,
            $this->getDefaultCategories());
      }
      return self::$categories;
   }

   /**
    * @brief returns the categories of the vcategories object
    * @return (array) $categories
    */
   public function getCategoryOptions() {
      $categories = $this->getVCategories()->categories();
      return $categories;
   }

   /**
    * scan events for categories.
    * @param $events VEVENTs to scan. null to check all events for the current user.
    */
   public function scanCategories($events = null) {
      $object_model = new PCalendarModelObject();
      $calendar_model = new PCalendarModelCalendar();

      if (is_null($events)) {
         $calendars = $calendar_model->allCalendars(OC_USER::getUser());
         if(count($calendars) > 0) {
            $events = array();
            foreach($calendars as $calendar) {
               if($calendar['userid'] === OC_USER::getUser()) {
                  $calendar_events = $object_model->all($calendar['id']);
                  $events = $events + $calendar_events;
               }
            }
         }
      }
      if(is_array($events) && count($events) > 0) {
         $vcategories = new OC_VCategories('event');
         $vcategories->delete($vcategories->categories());
         foreach($events as $event) {
            $vobject = OC_VObject::parse($event['calendardata']);
            if(!is_null($vobject)) {
               $object = null;
               if (isset($calendar->VEVENT)) {
                  $object = $calendar->VEVENT;
               } else
               if (isset($calendar->VTODO)) {
                  $object = $calendar->VTODO;
               } else
               if (isset($calendar->VJOURNAL)) {
                  $object = $calendar->VJOURNAL;
               }
               if ($object) {
                  $vcategories->loadFromVObject($event['id'], $vobject, true);
               }
            }
         }
      }
   }

   /**
    * check VEvent for new categories.
    * @see OC_VCategories::loadFromVObject
    */
   public function loadCategoriesFromVCalendar($id, OC_VObject $calendar) {
      $object = null;
      if (isset($calendar->VEVENT)) {
         $object = $calendar->VEVENT;
      } else
      if (isset($calendar->VTODO)) {
         $object = $calendar->VTODO;
      } else
      if (isset($calendar->VJOURNAL)) {
         $object = $calendar->VJOURNAL;
      }
      if ($object) {
         $this->getVCategories()->loadFromVObject($id, $object, true);
      }
   }

   /* Remove function getAccessClassOptions */
   /* Remove function getRepeatOptions */
   /* Remove function getEndOptions */
   /* Remove function getMonthOptions */
   /* Remove function getWeeklyOptions */
   /* Remove function getYearOptions */
   /* Remove function getByYearDayOptions */
   /* Remove function getByMonthOptions */
   /* Remove function getByWeekNoOptions */
   /* Remove function getByMonthDayOptions */
   /* Remove function getWeekofMonth */

   /**
    * @return (string) $timezone as set by user or the default timezone
    */
   public function getTimezone() {
      return OCP\Config::getUserValue(OC_USER::getUser(),
                  'calendar',
                  'timezone',
                  date_default_timezone_get());
   }

   /**
    * @brief Get the permissions for a calendar / an event
    * @param (int) $id - id of the calendar / event
    * @param (string) $type - type of the id (calendar/event)
    * @return (int) $permissions - CRUDS permissions
    * @param (string) $accessclass - access class (rfc5545, section 3.8.1.3)
    * @see OCP\Share
    */
   public function getPermissions($id, $type, $accessclass = '') {
      $permissions_all = OCP\PERMISSION_ALL;
      $object_model = new PCalendarModelObject();

      if($type == self::CALENDAR) {
         $calendar = $this->getCalendar($id, false, false);
         if($calendar['userid'] == OC_USER::getUser()) {
            return $permissions_all;
         } else {
            $sharedCalendar = OCP\Share::getItemSharedWithBySource('calendar', $id);
            if ($sharedCalendar) {
               return $sharedCalendar['permissions'];
            }
         }
      }
      elseif($type == self::EVENT) {
         if($object_model->getOwner($id) == OC_USER::getUser()) {
            return $permissions_all;
         } else {
            $object = $object_model->find($id);
            $sharedCalendar = OCP\Share::getItemSharedWithBySource('calendar', $object['calendarid']);
            $sharedEvent = OCP\Share::getItemSharedWithBySource('event', $id);
            $calendar_permissions = 0;
            $event_permissions = 0;
            if ($sharedCalendar) {
               $calendar_permissions = $sharedCalendar['permissions'];
            }
            if ($sharedEvent) {
               $event_permissions = $sharedEvent['permissions'];
            }
            if ($accessclass === 'PRIVATE') {
               return 0;
            } elseif ($accessclass === 'CONFIDENTIAL') {
               return OCP\PERMISSION_READ;
            } else {
               return max($calendar_permissions, $event_permissions);
            }
         }
      }
      return 0;
   }

   /*
    * @brief Get the permissions for an access class 
    * @param (string) $accessclass - access class (rfc5545, section 3.8.1.3)
    * @return (int) $permissions - CRUDS permissions
    * @see OCP\Share
    */
   public function getAccessClassPermissions($accessclass = '') {

      switch($accessclass) {
         case 'CONFIDENTIAL':
            return OCP\PERMISSION_READ;
         case 'PUBLIC':
         case '':
            return (OCP\PERMISSION_READ | OCP\PERMISSION_UPDATE | OCP\PERMISSION_DELETE);
         default:
            return 0;
      }
   }

   /**
    * @brief analyses the parameter for calendar parameter and returns the objects
    * @param (string) $calendarid - calendarid
    * @param (int) $start - unixtimestamp of start
    * @param (int) $end - unixtimestamp of end
    * @param (int) $jobid - jobid
    * @param (string) $share - share key to access one event
    * @return (array) $events
    */
   public function getrequestedEvents($calendarid, $start, $end, $jobid=0, $share='') {
      $object_model = new PCalendarModelObject();
      $events = array();
      if($calendarid == 'shared_events') {
         $singleevents = OCP\Share::getItemsSharedWith('event', OC_Share_Backend_Event::FORMAT_EVENT);
         foreach($singleevents as $singleevent) {
            $singleevent['summary'] .= ' (' . self::$l10n->t('by') .  ' ' . $object_model->getOwner($singleevent['id']) . ')';
            $events[] =  $singleevent;
         }
      }else{
         if (is_numeric($calendarid)) {
            $calendar = $this->getCalendar($calendarid);
            OCP\Response::enableCaching(0);
            OCP\Response::setETagHeader($calendar['ctag']);
            if ( !empty($share) ) {
               $events = $object_model->allInPeriod($calendarid, $start, $end, $jobid, $share);
            } else {
               $events = $object_model->allInPeriod($calendarid, $start, $end, $jobid, $share, $calendar['userid'] !== OC_USER::getUser());
            }
         } else {
            OCP\Util::emitHook('OC_Calendar', 'getEvents', array('calendar_id' => $calendarid, 'events' => &$events));
         }
      }
      return $events;
   }

   /**
    * @brief generates the output for an event which will be readable for our js
    * @param (mixed) $event - event object / array
    * @param (int) $start - DateTime object of start
    * @param (int) $end - DateTime object of end
    * @param (string) $share - share key to access one event
    * @return (array) $output - readable output
    */
   public function generateEventOutput(array $event, $start, $end, $share='') {
      $object_model = new PCalendarModelObject();
      \OCP\Util::writeLog('calendar', __METHOD__.' event: '.print_r($event['summary'], true), \OCP\Util::DEBUG);
      if(!isset($event['calendardata']) && !isset($event['vevent'])) {
         return false;
      }
      if(!isset($event['calendardata']) && isset($event['vevent'])) {
         $event['calendardata'] = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud's Internal iCal System\n"
            . $event['vevent']->serialize() .  "END:VCALENDAR";
      }
      $object = OC_VObject::parse($event['calendardata']);
      if(!$object) {
         \OCP\Util::writeLog('calendar', __METHOD__.' Error parsing event: '.print_r($event, true), \OCP\Util::DEBUG);
         return array();
      }

      $output = array();

      if($object->name === 'VEVENT') {
         $vevent = $object;
      } elseif(isset($object->VEVENT)) {
         $vevent = $object->VEVENT;
      } else {
         \OCP\Util::writeLog('calendar', __METHOD__.' Object contains not event: '.print_r($event, true), \OCP\Util::DEBUG);
         return $output;
      }
      $id = $event['id'];

      // do not show events with private or unknown access class
      if (isset($vevent->CLASS)
         && ($vevent->CLASS->value === 'CONFIDENTIAL'
         || $vevent->CLASS->value === 'PRIVATE'
         || $vevent->CLASS->value === ''))
      {
         return $output;
      }
      $object = $object_model->cleanByAccessClass($id, $object);

      // $allday = ($vevent->DTSTART->getDateType() == Sabre\VObject\Property\DateTime::DATE)?true:false;
      $allday = false;
      $last_modified = @$vevent->__get('LAST-MODIFIED');
      $lastmodified = ($last_modified)?$last_modified->getDateTime()->format('U'):0;
      $staticoutput = array('id'=>(int)$event['id'],
                  'title' => (!is_null($vevent->SUMMARY) && $vevent->SUMMARY->value != '')? $vevent->SUMMARY->value: self::$l10n->t('unnamed'),
                  'description' => isset($vevent->DESCRIPTION)?$vevent->DESCRIPTION->value:'',
                  'lastmodified'=>$lastmodified,
                  'allDay'=>$allday);

      foreach($object->getComponents() as $singleevent) {
         if(!($singleevent instanceof Sabre\VObject\Component\VEvent)) {
            continue;
         }
         $dynamicoutput = $object_model->generateStartEndDate($singleevent->DTSTART, $object_model->getDTEndFromVEvent($singleevent), $allday, self::$tz);
         $output[] = array_merge($staticoutput, $dynamicoutput);
      }

      \OCP\Util::writeLog('calendar', __METHOD__.' event: '.print_r($event['summary'], true) . ' done', \OCP\Util::DEBUG);
      return $output;
   }
}
